package util

import (
	"errors"
	"gitee.com/kudingc/role_proxy/common"
	"gitee.com/kudingc/role_proxy/models"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strings"
	"time"
)

var (
	RedisTknKeyPrefix = "tkn"
)

type Credentials struct {
	UserId   int    `json:"userId"`
	UserCode string `json:"user_code"`
	UserName string `json:"userName"`
}

type Claims struct {
	Credentials Credentials `json:"credentials"`
	jwt.StandardClaims
}

func CreateJwtToken(credentials Credentials) string {
	var timeDuration time.Duration

	if common.CheckTokenExpired {
		timeDuration = time.Duration(common.TokenExpirationTime)
	} else {
		timeDuration = time.Duration(10000000)
	}
	expirationTime := time.Now().Add(timeDuration * time.Minute)
	claims := &Claims{
		Credentials: credentials,
		StandardClaims: jwt.StandardClaims{
			// In JWT, the expiry time is expressed as unix milliseconds
			ExpiresAt: expirationTime.Unix(),
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// Create the JWT string
	tokenString, _ := token.SignedString([]byte(common.TokenEncryptionKey))
	return tokenString
}

// DecodeJwtToken 解析jwt，但是不关注过期时间。过期时间交由redis控制
// 即便token已经过期了，会报错，但是token中的数据还是解析出来了
func DecodeJwtToken(tknStr string) (*Claims, error) {
	claims := &Claims{}
	tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(common.TokenEncryptionKey), nil
	})
	if err != nil {
		return claims, err
	}
	if !tkn.Valid {
		return claims, errors.New("token is invalid")
	}
	return claims, nil
}

func RefreshJwtToken(jwt *Claims) string {
	newTkn := CreateJwtToken(jwt.Credentials)
	RedisSetStr(RedisTknKeyPrefix+jwt.Credentials.UserName, newTkn)
	return newTkn
}

// CheckAuthorization 检查Authorization
func CheckAuthorization(ctx *gin.Context) (*Claims, models.Res) {
	token := ctx.Request.Header["Authorization"]
	if token == nil {
		return nil, models.Res{
			State: models.StateErr,
			Msg:   "token is invalid",
		}
	}
	jwt, err := DecodeJwtToken(token[0])
	if err != nil {
		if strings.Contains(err.Error(), "token is expired") && !common.CheckTokenExpired {
			return jwt, models.Res{}
		} else {
			return nil, models.Res{
				State: models.StateErr,
				Msg:   err.Error(),
			}
		}
	}
	if jwt != nil && common.CheckTokenExpired {
		strTkn, err := RedisGetStr(RedisTknKeyPrefix + jwt.Credentials.UserCode + jwt.Credentials.UserName)
		if err != nil {
			return nil, models.Res{
				State: models.StateErr,
				Msg:   "redis is connect fail",
			}
		}
		if strTkn != token[0] {
			return nil, models.Res{
				State: models.StateErr,
				Msg:   "token is invalid",
			}
		}
	}
	return jwt, models.Res{}
}
